package com.sp.system.user.core.security;

import com.google.gson.Gson;
import com.sp.system.user.core.utils.ResponseUtil;
import com.sp.system.user.core.utils.enums.PermissionEnum;
import com.sp.system.user.core.utils.security.JwtTokenUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * jwt认证token
 * 每次请求接口时，就会进入这里验证token是否合法token，
 * 如果用户一直在操作，则token 过期时间会叠加；如果超过设置的过期时间未操作，则token 失效，需要重新登录。
 * @Author: chenanhai
 * @Date: 2019年9月3日
 */
@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }
    /**
     * 配置如何通过拦截器保护请求
     * 指定哪些请求需要认证，哪些请求不需要认证，以及所需要的权限
     * 通过调用authorizeRequests()和anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证
     *
     * 方法描述
     * anonymous()                                        允许匿名用户访问
     * authenticated()                                    允许经过认证的用户访问
     * denyAll()                                          无条件拒绝所有访问
     * fullyAuthenticated()                如果用户是完整的话（不是通过Remember-me功能认证的），就允许访问
     * hasAnyAuthority(String...)                 如果用户具备给定权限中的某一个的话，就允许访问
     * hasAnyRole(String...)                    如果用户具备给定角色中的某一个的话，就允许访问
     * hasAuthority(String)                     如果用户具备给定权限的话，就允许访问
     * hasIpAddress(String)                    如果请求来自给定IP地址的话，就允许访问
     * hasRole(String)                        如果用户具备给定角色的话，就允许访问
     * not()                               对其他访问方法的结果求反
     * permitAll()                           无条件允许访问
     * rememberMe()                          如果用户是通过Remember-me功能认证的，就允许访问
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//        super.doFilterInternal(request, response, chain);
        //获得accessToken
        String accessToken = request.getHeader(JwtTokenUtil.tokenHeader);
        log.debug("获取token 信息 doFilterInternal，request->{{}}",accessToken);
        if (null!=accessToken && accessToken.startsWith(JwtTokenUtil.tokenPrefix)) {
            try {
                UsernamePasswordAuthenticationToken authentication = getAuthentication(accessToken,request, response);
                SecurityContextHolder.getContext().setAuthentication(authentication);

            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {
            super.doFilterInternal(request, response, chain);
            return;
        }

        chain.doFilter(request, response);
    }
    private UsernamePasswordAuthenticationToken getAuthentication(String token,HttpServletRequest request, HttpServletResponse response) {
        if (!StringUtils.isEmpty(token)) {
            try {
//                log.info("解析对象UsernamePasswordAuthenticationToken{{}}{{}}",token.replace(JwtTokenUtil.tokenPrefix, ""),JwtTokenUtil.secret);
                Claims claims = JwtTokenUtil.getClaimsFromToken(token);

                //获取用户名
                String username = claims.getSubject();
                String userId=claims.getId();
                Date issuedAt = claims.getIssuedAt(); // 创建时间
                Date expiration = claims.getExpiration();
                if(new Date(System.currentTimeMillis() + JwtTokenUtil.refreshTokenExpiration* 60 * 1000).after(expiration)){
                    token = JwtTokenUtil.resfreshToken(token);
                }
                response.setHeader(JwtTokenUtil.tokenHeader, token);
                log.debug("解析对象{{}}{{}}{{}}",username,expiration,issuedAt);
                //获取权限（角色）
                List<GrantedAuthority> authorities = new ArrayList<>();
                String authority = (String) claims.get(JwtTokenUtil.authHeader);
                if(!StringUtils.isEmpty(authority)){
                    //authority="[{"authority":"common"}]"
                    List<Map<String,String>> authorityMap = new Gson().fromJson(authority, List.class);
                    for(Map<String,String> role : authorityMap){
                        if(!StringUtils.isEmpty(role)) {
                            authorities.add(new SimpleGrantedAuthority(role.get("role")));
                        }
                    }
                }
                if(!StringUtils.isEmpty(username)) {
                    //此处password不能为null
                    User principal = new User(username, "", authorities);

//                    request.setAttribute("loginId",userId);
//                    request.
                    return new UsernamePasswordAuthenticationToken(principal, userId, authorities);
                }
            } catch (ExpiredJwtException e) {
//               log.error("toekn超过有效期，请重新登");
                ResponseUtil.out(response, ResponseUtil.resultMap(PermissionEnum.TOKEN_INVALID_ERROR.getStatus(),PermissionEnum.TOKEN_INVALID_ERROR.getMessage()));
            } catch (Exception e){
                ResponseUtil.out(response, ResponseUtil.resultMap(PermissionEnum.TOKEN_INVALID_ERROR.getStatus(),PermissionEnum.TOKEN_INVALID_ERROR.getMessage()));
            }
        }
        return null;
    }


}
